#include"../MyTools/LogFilePostProcess.hpp"
#include"../MyTools/MatrixCsvReader.hpp"

/*xiamian ns3*/

#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/ndnSIM-module.h"
#include "ns3/gtk-config-store.h"
#include "ns3/point-to-point-module.h"//P2P
//#include "./Tools/matrix-csv-reader.hpp"

using namespace ns3;
using namespace ns3::ndn;

static void
TimeslotReader (const std::string fullPath)
{
    //static int now=1;
    AnnotatedTopologyReader topologyReader("", 1);
//    std::stringstream str;
//    str<<"timeslot/timeslot"<<num<<".txt";
    topologyReader.SetFileName(fullPath);
    topologyReader.Read();
    //now++;
    //if(now==25)now=1;
    //Simulator::Schedule (MilliSeconds(1000), &TimeslotReader);//yi miao yi ci
}

static void
OutputRuntime(void)
{
  std::cout<<"Now Simulator Run Time:"<<(Simulator::Now().GetSeconds())<<"\r";
  Simulator::Schedule (MilliSeconds(100),&OutputRuntime);
}

//0 LCE
//1 LCD
//2 Prob0.3
//3 Prob0.5
//4 Prob0.7
enum EnumCacheMode:uint64_t{
    LCE=0,
    LCD=1,
    Prob03=2,
    Prob05=3,
    Prob07=4,
    Betw=5,
    None=66,
};

/* Project Settings*/
const std::string m_myExpDataRootDir="./MyExpDatas/";//root
const std::string m_myLogsOutputDir="Outputs/";
const std::string m_myRawMatrialsDir="RawMatrials/";
/* Define By us*/
int m_WorldTime=4;//1-24
int m_csSize=30;//cs
char * m_zipfPeakOffest="0";// Feng Zhi Pian Yi
char * m_zipfValue="1.0";//zipf [0.7，1.3]
int m_reqFrequency=200;
int m_contentNumber=1000;
/* Link Settings */
char * m_byteRate="200Mbps";//Rates
char * m_linkDelay="100ms";//Delay
char * m_queueBase="10p";//QueueBase
EnumCacheMode m_cacheMode=LCD;

void DoSim()
{
    NS_LOG_COMPONENT_DEFINE("MyTest");
    /* Compute Files Paths*/
    std::stringstream
    m_myLogsFullPath,m_myCsvFileFullPath,
    m_myTimeslotFullPaths,m_myAppDelaysTracesFullPath,
    m_myPostProcessFullPath,m_myConfig;
    //m_myLogsFullPath.clear();
    std::time_t time= getTimeStamp();
    m_myLogsFullPath<<m_myExpDataRootDir<<m_myLogsOutputDir<<"RawDatas/mt_"<<time<<"_Res"<<m_WorldTime<<"t-out.log";//logs
    //m_myAppDelaysTracesFullPath.clear();
    m_myAppDelaysTracesFullPath<<m_myExpDataRootDir<<m_myLogsOutputDir<<"RawDatas/mt_"<<time<<"_Res"<<m_WorldTime<<"t-app-delays-trace.txt";//trace
    //m_myCsvFileFullPath.clear();
    m_myCsvFileFullPath<<m_myExpDataRootDir<<m_myRawMatrialsDir<<"24.csv";//csv
    //m_myTimeslotFullPaths.clear();
    m_myTimeslotFullPaths<<m_myExpDataRootDir<<m_myRawMatrialsDir<<"timeslot/timeslot"<<m_WorldTime<<".txt";//timeslots
    //m_myPostProcessFullPath.clear();
    m_myPostProcessFullPath<<m_myExpDataRootDir<<m_myLogsOutputDir<<"PostProcessData/pt_"<<1<<"_Res"<<m_WorldTime<<"t-output.txt";//outputFile
    /* Set Config = =*/
    m_myConfig<<"-timestamp="<<time<<std::endl;
    m_myConfig<<"-wTime="<<m_WorldTime<<std::endl;
    m_myConfig<<"-zipf="<<m_zipfValue<<std::endl;
    m_myConfig<<"-cacheMode="<<m_cacheMode<<std::endl;
    m_myConfig<<"-reqFre="<<m_reqFrequency<<std::endl;
    m_myConfig<<"-cNum="<<m_contentNumber<<std::endl;
    m_myConfig<<"-csSize="<<m_csSize<<std::endl;
    m_myConfig<<"-byteRates="<<m_byteRate<<std::endl;
    m_myConfig<<"-linkDelay="<<m_linkDelay<<std::endl;
    m_myConfig<<"-queueBase="<<m_queueBase<<std::endl;

    freopen(m_myLogsFullPath.str().c_str(), "w", stderr); //to files

    //freopen("/dev/console", "r", stdout);
    LogComponentEnable("MyTest", LOG_LEVEL_INFO);
    //for(int i=0;i<argc;i++)std::cout<<argv[i]<<std::endl;
    //std::cout<<"Waiting..."<<std::endl;
    LogComponentEnable("ndn-cxx.nfd.Forwarder", LOG_LEVEL_INFO);
    //Content Store
    LogComponentEnable("ndn-cxx.nfd.ContentStore", LOG_LEVEL_INFO);
    //ndn.Consumer:ndn.ConsumerZipfMandelbrot:ndn.Producer
    LogComponentEnable("ndn.Consumer", LOG_LEVEL_INFO);
    LogComponentEnable("ndn.ConsumerZipfMandelbrot", LOG_LEVEL_INFO);
    LogComponentEnable("ndn.Producer", LOG_LEVEL_INFO);


    //Config::SetDefault("ns3::PointToPointNetDevice::DataRate", StringValue(m_byteRate));
    //Config::SetDefault("ns3::PointToPointChannel::Delay", StringValue(m_linkDelay));
    //Config::SetDefault("ns3::QueueBase::MaxSize", StringValue(m_queueBase));


    //std::cout<<"Betw Pai Xu Ce Shi:"<<std::endl;
    //Test
    MatrixCsvReader matCsv(m_myCsvFileFullPath.str().c_str(),true);
    /*
    for (int i = 0; i < 66; i++)
    {
        for (int j = 0; j < 25; j++)
        {
            std::cout << matCsv.getElement(i, j);
            if(matCsv.getElement(i, j)<10)
                std::cout<<"  ";
            else
                std::cout<<" ";
        }
        std::cout << std::endl;
    }
    std::cout<<"=====END Betw Pai Xu Ce Shi====="<<std::endl;
*/
    //upon matrix nodesID start from 1, need -1

    TimeslotReader(m_myTimeslotFullPaths.str());//DuQu /Timeslot/...{para}.txt

    //ndn-cxx.nfd.Forwarder

    //Ptr<Node> maxBetwNode = nullptr;
    NodeContainer cacheNodes,serverNodes,forwarderNodes,clientNodes;

    vector<int> tempRandoms;

    for (int i = 7; i < 66; i++)//Random...
    {
        tempRandoms.push_back(i);
    }
    srand(2333);
    random_shuffle(tempRandoms.begin(), tempRandoms.end());// da luan

    for(int i=0;i<66;i++)
    {
        std::stringstream str;

        if(i<7)
        {

            str<<"Node"<<(matCsv.getElement(i, m_WorldTime)-1);//offest
          //  if(m_cacheMode==Betw&&i==0)
          //      maxBetwNode= Ptr<Node>( Names::Find<Node>(str.str()) );
          //  else
            cacheNodes.Add(Names::Find<Node>(str.str()));
            NS_LOG_INFO( "Add CacheNode:"<<str.str());
        }else {
            str<<"Node"<<(matCsv.getElement(tempRandoms[i-7], m_WorldTime)-1);//offest
            if (i == 7) {
                serverNodes.Add(Names::Find<Node>(str.str()));
                NS_LOG_INFO("Add SeverNode:" << str.str());
            } else if (i >= 8 && i <= 30) {
                clientNodes.Add(Names::Find<Node>(str.str()));
                NS_LOG_INFO("Add ClientNode:" << str.str());
            } else {
                forwarderNodes.Add(Names::Find<Node>(str.str()));
                NS_LOG_INFO("Add ForwarderNodes:" << str.str());
            }
        }
    }


    //clientNodes.Create(3);//3
    //PointToPointHelper p2pHelper;//draw line

    //p2pHelper.SetDeviceAttribute("DataRate",StringValue("100Mbps"));
    //p2pHelper.SetChannelAttribute("Delay",StringValue("10ms"));
    //for(int i=0;i<2;i++)p2pHelper.Install(clientNodes.Get(i),clientNodes.Get(i+1));
    //p2pHelper.Install(forwarderNodes.Get(5),clientNodes.Get(2));

    StackHelper ndnHelper;
    ndnHelper.setPolicy("nfd::cs::lru");
    ndnHelper.setCsSize(m_csSize);

    ndnHelper.Install(cacheNodes);
    ndnHelper.Install(serverNodes);
    if(m_cacheMode!=Betw)
    {
        ndnHelper.Install(forwarderNodes);
       //StrategyChoiceHelper::Install(forwarderNodes,"/prefix", "/localhost/nfd/strategy/best-route");
    }
    //StrategyChoiceHelper::Install(serverNodes,"/prefix", "/localhost/nfd/strategy/multicast");
    //StrategyChoiceHelper::Install(cacheNodes,"/prefix", "/localhost/nfd/strategy/multicast");
    //ndnHelper.
    ndnHelper.setCsSize(0);
    //CsTracer::InstallAll("cs-trace.txt");
    //ndnHelper.setPolicy("nfd::cs::nocache");
    ndnHelper.Install(clientNodes);
    if(m_cacheMode==Betw)
    {
        ndnHelper.Install(forwarderNodes);
        //StrategyChoiceHelper::Install(forwarderNodes,"/prefix", "/localhost/nfd/strategy/multicast");
    }

    StrategyChoiceHelper::InstallAll("/prefix", "/localhost/nfd/strategy/best-route");
    //StrategyChoiceHelper::Install(forwarderNodes,"/prefix", "/localhost/nfd/strategy/best-route");
    //StrategyChoiceHelper::InstallAll("/prefix", "/localhost/nfd/strategy/best-route");
    //localhost/nfd/strategy/ncc
    GlobalRoutingHelper ndnGlobalRoutingHelper;
    ndnGlobalRoutingHelper.InstallAll();


    // Install CCNx applications
    std::string prefix = "/prefix";
    AppHelper consumerHelper("ns3::ndn::ConsumerZipfMandelbrot");
    // ndn::AppHelper consumerHelper ("ns3::ndn::ConsumerCbr");

    /*Defined by us*/
    consumerHelper.SetPrefix(prefix);
    consumerHelper.SetAttribute("Frequency", StringValue( to_string(m_reqFrequency).c_str() )); // 200 interests a second
    consumerHelper.SetAttribute("NumberOfContents", StringValue(to_string(m_contentNumber).c_str())); // 1000 different contents
    consumerHelper.SetAttribute("q", StringValue(m_zipfPeakOffest));// zipf
    consumerHelper.SetAttribute("s", StringValue(m_zipfValue));
    //0 LCE
    //1 LCD
    //2 Prob0.3
    //3 Prob0.5
    //4 Prob0.7
    //5 Betw
    consumerHelper.SetAttribute("CacheMode", StringValue(to_string(m_cacheMode)));
    // consumerHelper.SetAttribute ("Randomize", StringValue ("uniform")); // 100 interests a second

    consumerHelper.Install(clientNodes);//client

    AppHelper producerHelper("ns3::ndn::Producer");

    /*Defined by us*/
    producerHelper.SetPrefix(prefix);
    //producerHelper.SetAttribute("PayloadSize", StringValue("1024"));

    producerHelper.Install(serverNodes);
    ndnGlobalRoutingHelper.AddOrigins(prefix, serverNodes);

    // Calculate and install FIBs
    GlobalRoutingHelper::CalculateRoutes();
    AppDelayTracer::InstallAll(m_myAppDelaysTracesFullPath.str().c_str());

    //Names::Find<Node>("Node");
    //GUI
    //GtkConfigStore config;
    //config.ConfigureDefaults ();
    //config.ConfigureAttributes ();

    OutputRuntime();
    Simulator::Stop(Seconds(10.0));
    Simulator::Run();

    m_myConfig<<"-finalRunTime="<<Simulator::Now().GetMicroSeconds()<<std::endl;

    Simulator::Destroy();
    fclose(stderr);//close stream
    LogFilePostProcess lfpp(
            m_myPostProcessFullPath.str(),
            m_myLogsFullPath.str(),
            m_myAppDelaysTracesFullPath.str(),
            m_myConfig.str()
            );
}

int
main(int argc, char* argv[])
{

    CommandLine cmd;

    double  p_zipf= stod(m_zipfValue);
    cmd.AddValue("zipf", "zipfValue", p_zipf);

    static uint64_t p_cacheMode=m_cacheMode;
    cmd.AddValue("cm", "cacheMode", p_cacheMode);

    static int p_freq=m_reqFrequency;
    cmd.AddValue("freq", "frequency", p_freq);

    static int p_cnum=m_contentNumber;
    cmd.AddValue("cnum", "ContentNumber", p_cnum);

//    static int p_cnum=m_contentNumber;
//    cmd.AddValue("cnum", "ContentNumber", p_cnum);

    cmd.Parse(argc, argv);

    static string p_zipfval=to_string(p_zipf).substr(0,3);//string life time !
    m_zipfValue= (char *)(p_zipfval.c_str());

    m_cacheMode=(EnumCacheMode)p_cacheMode;

    m_reqFrequency=p_freq;
    m_contentNumber=p_cnum;

    std::cout<<"NowZipf:"<<m_zipfValue<<std::endl;
    std::cout<<"NowCacheMode:"<<m_cacheMode<<std::endl;
    std::cout<<"NowReqFrequency:"<<m_reqFrequency<<std::endl;
    std::cout<<"NowContentNumbers:"<<m_contentNumber<<std::endl;
    DoSim();

}